#!/usr/bin/python
import dbus
import sys
import signal
from ConfigParser import *
import joystick
import threading
import Queue


try:
	import pygame
except:
	popen("kdialog --sorry 'Pygame is required for this script.'")
	raise
try:
	from PyQt4 import QtGui, QtCore
except:
	popen("kdialog --sorry 'PyQt (Qt bindings for Python) is required for this script.'")
	raise



bus=dbus.SessionBus( )
proxy=bus.get_object( 'org.kde.amarok', '/Player')
method=dbus.Interface( proxy ,'org.freedesktop.MediaPlayer')

prog_version='v020100804'

joycmds = {
	"Mute/Unmute": ["Mute",''],
	"Next Track": ["Next",''],
	"Play/Pause": ["PlayPause",''],
	"Prev Track": ["Prev",''],
	"Seek Backward 30s": ["PositionSet",'method.PositionGet()-30000'],
	"Seek Backward 5s": ["PositionSet",'method.PositionGet()-5000'],
	"Seek Forward 30s": ["PositionSet",'method.PositionGet()+30000'],
	"Seek Forward 5s": ["PositionSet",'method.PositionGet()+5000'],
	"Stop": ["Stop",''],
	"Volume Down": ["VolumeDown",'5'],
	"Volume Up": ["VolumeUp",'5'],
}

joylist = []
for f in joycmds.keys():
    joylist.append(f)

joylist.sort()
joylist.append("Nothing")




def send_action(action):
	if action[1] == '':
		exec("method.%s()"%(action[0]))
	else:
		exec("method.%s(%d)"%(action[0],eval(action[1])))



class ConfigDialog(QtGui.QDialog):

	def __init__(self, joystickconfig):
		QtGui.QDialog.__init__(self)
		self.setWindowTitle("Joystick Script - amaroK")
		
		nrows = 6
		if (pygame.joystick.Joystick(0).get_numbuttons() > nrows):
			nrows = pygame.joystick.Joystick(0).get_numbuttons()

		self.lay = QtGui.QGridLayout(self)

		# right side
		self.buttons = []
		for j in range(0, pygame.joystick.Joystick(0).get_numbuttons()):
			newlabel = QtGui.QLabel("Button %s: " % str(j + 1), self)
			newbutton = QtGui.QComboBox(self)

			self.buttons.append(newbutton)
			self.comboDefault(newbutton, joystickconfig.buttoncmd[j])
			self.lay.addWidget(newlabel, j, 5)
			self.lay.addWidget(newbutton, j, 6)

		# left side
		dirlabel = QtGui.QLabel("Directionals: ", self)
		self.lay.addWidget(dirlabel,  0, 3)
		verlabel = QtGui.QLabel(prog_version, self)
		self.lay.addWidget(verlabel, nrows - 1, 3)

		self.dir = {}
		self.dir['up'] = QtGui.QComboBox(self)
		self.dir['down'] = QtGui.QComboBox(self)
		self.dir['left'] = QtGui.QComboBox(self)
		self.dir['right'] = QtGui.QComboBox(self)

		for k in self.dir:
			self.comboDefault(self.dir[k], joystickconfig.dircmd[k])

		self.lay.addWidget(self.dir['up'], 1,  2)
		self.lay.addWidget(self.dir['left'], 2, 0)
		self.lay.addWidget(self.dir['right'], 2,  3)
		self.lay.addWidget(self.dir['down'], 3,  2)

		# ok / cancel
		self.ok = QtGui.QPushButton(self)
		self.ok.setText("Ok")
		self.lay.addWidget(self.ok, nrows - 2,  0)

		self.cancel = QtGui.QPushButton(self)
		self.cancel.setText("Cancel")
		self.cancel.setDefault(True)
		self.lay.addWidget(self.cancel, nrows - 2,  3)

		self.connect(self.ok, QtCore.SIGNAL("clicked()"), self.save)
		self.connect(self.cancel, QtCore.SIGNAL("clicked()"), self, QtCore.SLOT("reject()"))

		self.adjustSize()

	def __del__(self):
		print "ConfigDialog dtor"

	def comboDefault(self, widget, cmd):
		idx = 0
		j = 0
		for name in joylist:
			widget.insertItem(j,name)
			if (name == cmd):
				idx = j
			j = j + 1
		widget.setCurrentIndex(idx)

	def save(self):
		self.file = file("joystickrc", 'w')

		self.config = ConfigParser()
		self.config.add_section("General")

		for k in self.dir:
			self.config.set("General", "dir%s" % k, self.dir[k].currentText())

		for j in range(0, pygame.joystick.Joystick(0).get_numbuttons()):
			self.config.set("General", "button%s" % str(j + 1), self.buttons[j].currentText())

		self.config.write(self.file)
		self.file.close()

		self.accept()

class Joystick(QtGui.QApplication):

	def __init__(self, args):
		QtGui.QApplication.__init__(self, args)
		pygame.joystick.Joystick(0).init()
		
		self.queue = Queue.Queue()
		self.queue2 = Queue.Queue()
		
		self.readSettings()
		self.timer_id = self.startTimer(100)

		self.t1 = threading.Thread(target = self.readStdin)
		self.t1.start()
		self.t2 = threading.Thread(target = self.joystickControl)
		self.t2.start()


	def readSettings(self):
		config = ConfigParser()
		config.read("joystickrc")

		# assume nothing
		self.dircmd = {}
		self.dircmd['up'] = "Nothing"
		self.dircmd['down'] = "Nothing"
		self.dircmd['left'] = "Nothing"
		self.dircmd['right'] = "Nothing"

		self.buttoncmd = []
		for j in range(0, pygame.joystick.Joystick(0).get_numbuttons()):
			self.buttoncmd.append("Nothing")
		# read in settings
		try:
			for k in self.dircmd:
				self.dircmd[k] = config.get("General", "dir%s" % k)

			for j in range(0, pygame.joystick.Joystick(0).get_numbuttons()):
				self.buttoncmd[j] = config.get("General", "button%s" % str(j + 1))

		except:
			pass

	def configure(self):
		print "Joystick Script: configuration"

		self.dia = ConfigDialog(self)
		self.connect(self.dia, QtCore.SIGNAL("accepted()"), self.readSettings)
		self.dia.show()




############################################################################
# Stdin-Reader Thread
############################################################################

	def readStdin(self):
		while True:
			line = sys.stdin.readline()

			if line:
				#import pdb;pdb.set_trace()
				self.queue.put_nowait(line)
			else:
				break


############################################################################
# Joystick Event Thread
############################################################################

	def joystickControl(self):
		while True:
			e = pygame.event.wait()
			if (e.type == pygame.JOYAXISMOTION or e.type == pygame.JOYBUTTONDOWN):
				self.queue2.put_nowait(e)


############################################################################
# Command Handling
############################################################################

	def timerEvent(self, event):

		if not self.queue.empty():
			string = self.queue.get_nowait()

			if 'configure' in string:
				self.configure()

		if not self.queue2.empty():
			self.handleJoyEvent(self.queue2.get_nowait())


	def handleJoyEvent(self, e):
		if e.type == pygame.JOYAXISMOTION:
			if (e.dict['axis'] == 0):
				if (e.dict['value'] > 0):
					self.runCmd(self.dircmd['right'])
				elif (e.dict['value'] < 0):
					self.runCmd(self.dircmd['left'])
			elif (e.dict['axis'] == 1):
				if (e.dict['value'] > 0):
					self.runCmd(self.dircmd['down'])
				elif (e.dict['value'] < 0):
					self.runCmd(self.dircmd['up'])
		elif e.type == pygame.JOYBUTTONDOWN:
			self.runCmd(self.buttoncmd[e.dict['button']]);
		else:
			pass

	def runCmd(self, cmd):
		try:
			action = joycmds[cmd]
			send_action(action)
		except:
			pass


def onStop(signum, stackframe):
	# or else script doesn't die
	pygame.display.quit()
	signal.signal(15, signal.SIG_DFL)
	kill(getpid(), 15)


def main():
	pygame.joystick.init()
	pygame.display.init()

	if not pygame.joystick.get_count():
		popen("kdialog --sorry 'No joystick detected'")
		raise ValueError	

	app = Joystick(sys.argv)
	app.exec_()

if __name__ == "__main__":
	signal.signal(15, onStop)
	main()

